#include <stdio.h>
#include <modbus.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include "msg_queue_peer.h"
#include "shmem.h"
#include "cJSON.h"
#include "list.h"

modbus_t *ctx;
#define MAGIC_ID 'j'
#define IP "192.168.50.235"

// 定义共享内存结构体
union val_t
{
    int b_val;   // bool类型存储空间
    int i_val;   // 整形值存储空间
    float f_val; // 浮点值存储空间
};

struct std_node
{
    int key;             // 唯一键值
    int type;            // 数据点类型
    int dev_type;        // 数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; // 变化上报后需要更新旧值
    union val_t new_val; // 从共享内存取出最新数据，放到new_val中
    int ret;             // 默认为-1，采集成功后设置为0，采集失败再置-1
};
// 定义消息队列结构体
typedef struct
{
    long type; // 1
    char command[1024];
} Cmd;
// 将数据点的key和addr对应关系做缓存链表（数组）
struct mb_node
{
    int key;        // 唯一键值
    char name[128]; // 数据点名称
    int type;       // 数据点类型
    int addr;
};

struct mb_node_list
{
    struct mb_node node;
    struct list_head list;
};

int main(int argc, char const *argv[])
{
    // 解析点表并创建key和addr的映射关系的内核链表
    FILE *fp;
    int table_addr[32];      // 寄存器地址
    int table_key[32];       // 唯一键值
    int table_type[32];      // 数据点类型
    char table_name[32][32]; // 数据点名称

    int key[32];
    int addr[32];
    int i = 0; // 数据点数量

    char buf[2048] = "";
    struct mb_node_list *mbnode_array;
    mbnode_array = malloc(sizeof(struct mb_node_list) * 32);
    // 创建key和addr的映射关系的内核链表
    struct list_head head; // 定义一个头结点
    INIT_LIST_HEAD(&head); // 初始化头结点
    // 读取json文件中的数据
    fp = fopen("/mnt/config/node.json", "r");
    if (fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    fread(buf, 2048, 1, fp);

    // 将json字符串转换为cJSON树结构
    cJSON *root = cJSON_Parse(buf);
    cJSON *mbus = cJSON_GetObjectItem(root, "modbus");
    cJSON *data = cJSON_GetObjectItem(mbus, "data");

    // 获取数组大小
    int size = cJSON_GetArraySize(data);
    // 遍历数组,将数据点的key和addr对应关系做缓存链表（数组）
    for (int k = 0; k < size; k++)
    {
        cJSON *item[size];
        item[k] = cJSON_GetArrayItem(data, k); // 获取数组元素

        cJSON *key_node[size];
        key_node[k] = cJSON_GetObjectItem(item[k], "key"); // 获取key值
        table_key[k] = key_node[k]->valueint;
        mbnode_array[k].node.key = table_key[k];
        cJSON *addr_node[size];
        addr_node[k] = cJSON_GetObjectItem(item[k], "addr"); // 获取addr值
        table_addr[k] = addr_node[k]->valueint;
        mbnode_array[k].node.addr = table_addr[k];
        cJSON *name_node[size];
        name_node[k] = cJSON_GetObjectItem(item[k], "name"); // 获取name值
        strcpy(table_name[k], name_node[k]->valuestring);
        strcpy(mbnode_array[k].node.name, table_name[k]);
        cJSON *type_node[size];
        type_node[k] = cJSON_GetObjectItem(item[k], "type"); // 获取type值
        table_type[k] = type_node[k]->valueint;
        mbnode_array[k].node.type = table_type[k];
        list_add(&mbnode_array[k].list, &head);
    }

    // 1.创建实例
    ctx = modbus_new_tcp(IP, 502);
    int msgid; // 消息队列id
    if (ctx == NULL)
    {
        printf("modbus new err");
        return -1;
    }
    // 2.设置从机id
    modbus_set_slave(ctx, 1);
    // 3.建立连接
    if (modbus_connect(ctx) < 0)
    {
        perror("connect err");
        return -1;
    }
    printf("设备connect okk\n");
    struct shm_param para;
    struct mb_node_list *tmp;
    struct list_head *pos;
    list_for_each(pos, &head)
    {
        tmp = list_entry(pos, struct mb_node_list, list);
        key[i] = tmp->node.key;
        addr[i] = tmp->node.addr;
        i++;
    }
    u_int8_t dest[32] = {0};
    int *p = NULL; // 映射后的起始地址
    // 映射共享内存
    if (shm_init(&para, "data", 1024) < 0)
    {
        printf("shm init err\n");
        return NULL;
    }
    p = shm_getaddr(&para);
    int node_count = *(int *)p;
    printf("%d\n", node_count);
    // 共享内存首地址是结构体节点个数
    struct std_node *data_p = (struct std_node *)(p + 1);
    while (1)
    {
        for (int num = 0; num < i; num++)
        {
            if (addr[num] >= 1 && addr[num] <= 9999) // 1-9999读写单个线圈
            {
                // 读取寄存器的值
                modbus_read_bits(ctx, addr[num], 1, &dest[num]);
                //  功能：读取线圈状态，可读取多个连续线圈的状态（对应功能码为0x01）
                //  参数：
                //  ctx   ：Modbus实例
                //  addr ：寄存器起始地址
                //  nb    ：寄存器个数
                //  dest ：得到的状态值
                //  返回值：成功：读到的数量
                //  失败：-1
                // 遍历共享内存找到对应的key节点，将dest写到共享内存中
                struct std_node *tmp_std = NULL;
                for (int j = 0; j < node_count; j++)
                {
                    tmp_std = data_p + j;
                    if (tmp_std->key == key[num])
                    {
                        tmp_std->old_val.b_val = tmp_std->new_val.b_val;
                        tmp_std->new_val.b_val = (int)dest[num];
                        printf("%d\n", tmp_std->new_val.b_val);
                    }
                }
            }
            // 获取消息队列消息
            Cmd msg_rcv; // 存放读取消息的空间
            int ret = msg_queue_recv("mb_command", &msg_rcv, sizeof(Cmd), 1, IPC_NOWAIT);
            if (ret >= 0)
            {
                cJSON *root = cJSON_Parse(msg_rcv.command);
                cJSON *msg_key = cJSON_GetObjectItem(root, "key");
                cJSON *val = cJSON_GetObjectItem(root, "val");
                if (msg_key->valueint == key[num])
                {
                    if (val->valueint == 1)
                    {
                        modbus_write_bit(ctx, addr[num], 1);
                    }
                    else if (val->valueint == 0)
                    {
                        modbus_write_bit(ctx, addr[num], 0);
                    }
                }
                cJSON_Delete(root); // 释放JSON资源
            }
            else if (errno != ENOMSG)
            {
                perror("msg_queue_recv error");
            }
        }
        sleep(1);
    }
    // 5.关闭实例
    modbus_close(ctx);
    modbus_free(ctx);
    cJSON_Delete(root); // 释放JSON资源
    return 0;
}
